package com.exist.simple.demo.Utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassReflectUtils {

    private static Logger logger = LoggerFactory.getLogger(ClassReflectUtils.class);

    //获取class中用户自定义的公共方法
    public static List<Map<String, List<String>>> getClassNameAndMethodList(List<Class> clazzs) {
        List<Map<String, List<String>>> classNameAndMethodList = new ArrayList<>();

        if (clazzs != null && clazzs.size() > 0) {
            for (Class clazz : clazzs) {
                Map<String, List<String>> classNameAndMethod = new HashMap<>();
                List<String> methodList = new ArrayList<>();

                getClassMethodName(clazz, methodList);
                classNameAndMethod.put(clazz.getPackage().getName() + "." + clazz.getSimpleName(), methodList);
                classNameAndMethodList.add(classNameAndMethod);
            }
        }
        return classNameAndMethodList;
    }

    //筛选class中用户自定义的公共方法
    private static void getClassMethodName(Class clazz, List<String> methodList) {

        Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz);
        for (Method method : methods) {
            if (ReflectionUtils.USER_DECLARED_METHODS.matches(method)) {
                logger.info("==user method name: "+method.getName() + "==user method modifiers: "+method.getModifiers());
                if (1 == method.getModifiers()) {
                    methodList.add(method.getName());
                }
            }
        }
    }

    /**
     * 获得包下面的所有的class
     *
     * @param pack
     *            package完整名称
     * @return List包含所有class的实例
     */
    public static List<Class> getClasssFromPackage(String pack) {

        List<Class> clazzs = new ArrayList<Class>();
        // 是否循环搜索子包
        boolean recursive = true;
        // 包名字
        String packageName = pack;
        // 包名对应的路径名称
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs;

        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();

                if ("file".equals(protocol)) {
                    logger.info("file类型的扫描");
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findClassInPackageByFile(packageName, filePath, recursive, clazzs);
                } else if ("jar".equals(protocol)) {
                    logger.info("jar类型的扫描");
                    String jarPath = getJarPathFromLocalUrl(url);
                    logger.info("jarPath: " + jarPath + ", packageDirName: " + packageDirName);
                    clazzs = getClasssFromJarFile(jarPath, packageDirName);
                }
            }

        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return clazzs;
    }

    /**
     * 在package对应的路径下找到所有的class
     *
     * @param packageName
     *            package名称
     * @param filePath
     *            package对应的路径
     * @param recursive
     *            是否查找子package
     * @param clazzs
     *            找到class以后存放的集合
     */
    public static void findClassInPackageByFile(String packageName, String filePath, final boolean recursive, List<Class> clazzs) {

        File dir = new File(filePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        // 在给定的目录下找到所有的文件，并且进行条件过滤
        File[] dirFiles = dir.listFiles(new FileFilter() {

            @Override
            public boolean accept(File file) {
                boolean acceptDir = recursive && file.isDirectory();// 接受dir目录
                boolean acceptClass = file.getName().endsWith("class");// 接受class文件
                return acceptDir || acceptClass;
            }
        });

        for (File file : dirFiles) {
            if (file.isDirectory()) {
                findClassInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, clazzs);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 从jar文件中读取指定目录下面的所有的class文件
     *
     * @param jarPath
     *            jar文件存放的位置
     * @param packageDirName
     *            指定的文件目录
     * @return 所有的的class的对象
     */
    public static List<Class> getClasssFromJarFile(String jarPath, String packageDirName) {

        List<Class> clazzs = new ArrayList<Class>();
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(jarPath);
            List<JarEntry> jarEntryList = new ArrayList<JarEntry>();
            Enumeration<JarEntry> ee = jarFile.entries();

            while (ee.hasMoreElements()) {
                JarEntry entry = (JarEntry) ee.nextElement();
                // 过滤我们出满足我们需求的东西
                if (entry.getName().contains(packageDirName) && entry.getName().endsWith(".class")) {
                    jarEntryList.add(entry);
                }
            }

            for (JarEntry entry : jarEntryList) {
                String classNameTemp = entry.getName();
                if (classNameTemp.contains("BOOT-INF/")) {
                    classNameTemp = classNameTemp.replace("BOOT-INF/", "");
                }
                if (classNameTemp.contains("classes/")) {
                    classNameTemp = classNameTemp.replace("classes/", "");
                }
                String className = classNameTemp.replace('/', '.');
                className = className.substring(0, className.length() - 6);
                logger.info("getClasssFromJarFile className: {}", className);
                clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(className));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return clazzs;
    }

    /**
     * 通过流将jar中的一个文件的内容输出
     *
     * @param jarPath
     *            jar文件存放的位置
     * @param filePath
     *            指定的文件目录
     */
    public static void getStream(String jarPath, String filePath) {
        try {
            JarFile jarFile = new JarFile(jarPath);
            Enumeration<JarEntry> ee = jarFile.entries();
            List<JarEntry> jarEntryList = new ArrayList<JarEntry>();

            while (ee.hasMoreElements()) {
                JarEntry entry = (JarEntry) ee.nextElement();
                // 过滤我们出满足我们需求的东西，这里的fileName是指向一个具体的文件的对象的完整包路径，比如com/mypackage/test.txt
                if (entry.getName().startsWith(filePath)) {
                    jarEntryList.add(entry);
                }
            }

            InputStream in = jarFile.getInputStream(jarEntryList.get(0));
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String s = "";

            while ((s = br.readLine()) != null) {
                logger.info("getStream s: {}", s);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    public static String getJarPathFromLocalUrl(URL url) {
        String jarPath = "";
        try {
            jarPath = url.getPath();
            // 去掉路径信息中的协议名"file:"

            int pos = jarPath.indexOf("file:");
            if (pos > -1)
                jarPath = jarPath.substring(pos + 5);

            if (jarPath.contains(".jar"))
                jarPath = jarPath.substring(0, jarPath.indexOf(".jar") + 4);

            jarPath = URLDecoder.decode(jarPath, "utf-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return jarPath;
    }

    public static void main(String[] args) {

        String packageName = "com.exist.simple.demo.instruction";
        List<Class> clazzs = null;

        clazzs = getClasssFromPackage(packageName);

        /*String jarPath = "E:\\git_repository\\git_test\\mytest_my\\mytest\\demo\\target\\demo-0.0.1-SNAPSHOT.jar";
        String packageDirName = "com/exist/simple/demo/instruction";
        clazzs = getClasssFromJarFile(jarPath, packageDirName);*/

        for (Class clazz : clazzs) {
            System.out.println(clazz.getName());
        }

        /*getStream("Jar文件的路径", "Jar文件总的一个具体文件的路径");*/
    }

}
